package com.lipaluma.field.mapper.multiple.impl;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lipaluma.annotations.validations.Validate;
import com.lipaluma.context.Context;
import com.lipaluma.field.mapper.mapping.FieldMappingInfo;
import com.lipaluma.field.mapper.mapping.MappingOptions;
import com.lipaluma.field.mapper.multiple.AbstractStandardMultipleFieldMapper;
import com.lipaluma.field.validator.ChainValidator;
import com.lipaluma.field.validator.ValidationBasicOptions;
import com.lipaluma.field.validator.ValidatorFactory;
import com.lipaluma.xml.mapping.XmlFieldMapping;

/**
 * MapColumn allows concatenate several columns of the entry file before injecting in the field of the POJO.
 * <p>
 * The type of the field must be a String.<br/>
 * But, for associate the type String with that {@link ConcatMultipleFieldMapper}, we have to add the annotation {@link MapWith} with the type CONCAT.<br/>
 * And then, the annotation MapColumn is no more necessary, but the annotation {@link ConcatMultipleMappingOnString} must be present.
 * 
 * @author Mario
 */
public class ConcatMultipleFieldMapper extends AbstractStandardMultipleFieldMapper {
	private static Logger LOG = LoggerFactory.getLogger(ConcatMultipleFieldMapper.class);
	private String separator= "";

	public ConcatMultipleFieldMapper() {}
	public ConcatMultipleFieldMapper(List<FieldMappingInfo> mappings) {
		this(mappings, "", false);
	}
	public ConcatMultipleFieldMapper(List<FieldMappingInfo> mappings, String separator) {
		this(mappings, separator, false);
	}
	public ConcatMultipleFieldMapper(List<FieldMappingInfo> mappings, String separator, boolean keepAll) {
		super(mappings, keepAll);
		this.separator = separator;
	}

	@Override
	protected void init(MappingOptions options) {
		if(separator == null || separator.isEmpty())
			separator = options.getSeparator();
	}
	
	@Override
	protected ChainValidator createChainValidator(Context context, Field field) {
		ValidationBasicOptions validationOptions = ValidationBasicOptions.builder().byAnnotation(field.getAnnotation(Validate.class)).build();
		return ValidatorFactory.createNewChainValidator(context, field, validationOptions);
	}

	@Override
	protected ChainValidator createChainValidator(Context context, Field field, XmlFieldMapping mapping) {
		return ValidatorFactory.createNewChainValidator(context, field, mapping);
	}
	
	@Override
	protected Object parseExtractedValues(List<String> values) {
		if(values == null)
			return null;
		if(values.isEmpty())
			return StringUtils.EMPTY;
		StringBuilder concat = new StringBuilder(String.valueOf(values.get(0)));
		for (int i=1; i<values.size(); i++) {
			concat.append(separator).append(values.get(i));
		}
		return concat.toString();
	}

	@Override
	protected List<String> formatValue(Object value) {
		if(StringUtils.isEmpty(separator)) {
			LOG.warn("Warning on field '"+field.getName()+"' : impossible to retrieve all multiple values to marshall on a string concatened without separator.");
			return Arrays.asList(value.toString());
		}
		if(keepAll)
			return Arrays.asList(StringUtils.splitPreserveAllTokens(value.toString(), separator));
		else
			return Arrays.asList(StringUtils.split(value.toString(), separator));
	}
	
	@Override
	public String getDescription() {
		return "";
	}
}